<template>
    <div>
        <el-form label-width="120px" :model="spuFrom" :rules="rules" ref="ruleFormRef" status-icon>
            <el-form-item label="SPU名称" prop="spuName">
                <el-input placeholder="请输入SPU名称" style="width: 250px" v-model="spuFrom.spuName"></el-input>
            </el-form-item>
            <el-form-item label="SPU品牌" prop="tmId">
                <el-select placeholder="请选择SPU品牌" style="width: 150px" v-model="spuFrom.tmId">
                    <el-option v-for="item in trademarkList" :key="item.id" :label="item.tmName"
                        :value="item.id as number" />
                </el-select>
            </el-form-item>
            <el-form-item label="SPU描述" prop="description">
                <el-input placeholder="请输入SPU描述" type="textarea" :rows="5" style="width: 70%"
                    v-model="spuFrom.description"></el-input>
            </el-form-item>
            <el-form-item label="SPU照片" prop="spuImageList">
                <el-upload action="/api/admin/product/fileUpload" v-model:file-list="fileList"
                    :on-preview="handlePictureCardPreview" :on-remove="handleRemove" list-type="picture-card"
                    :on-error="handleError" :before-upload="beforeAvatarUpload" :on-success="handleAvatarSuccess">
                    <el-icon><i-ep-Plus /></el-icon>
                </el-upload>

                <el-dialog v-model="dialogVisible">
                    <div style="display: flex; justify-content: center; align-items: center; height: 100%;">
                        <img w-full :src="dialogImageUrl" alt="Preview Image"
                            style="max-width: 100%; max-height: 100%;" />
                    </div>
                </el-dialog>
            </el-form-item>
            <el-form-item label="SPU销售属性" prop="spuSaleAttrList">
                <div style="width: 100%;">
                    <el-select :placeholder="unSelectSaleAttr.length ? `还有${unSelectSaleAttr.length}个未选择` : `暂无数据可选择`"
                        style="width: 150px" v-model="baseSaleAttrRef" value-key="id">
                        <el-option v-for="item in unSelectSaleAttr" :key="item.id" :label="item.name" :value="item" />
                    </el-select>
                    <el-button type="primary" style="margin-left: 10px" icon="Plus" @click="addSaleAttr"
                        :disabled="!baseSaleAttrRef">添加属性</el-button>
                </div>
                <el-table border style="margin-top: 10px;width: 88%;" :data="spuFrom.spuSaleAttrList" stripe>
                    <el-table-column align="center" label="序号" width="80" type="index" />
                    <el-table-column align="center" prop="saleAttrName" label="销售属性名称" width="150" />
                    <el-table-column align="center" label="销售属性值">
                        <template #default="{ row, $index }">
                            <div class="dynamic-tags">
                                <el-tag v-for="(tag, index) in row.spuSaleAttrValueList" :key="index" closable
                                    :disable-transitions="false" @close="handleClose($index, tag)" type="primary">
                                    {{ tag.saleAttrValueName }}
                                </el-tag>
                                <el-input v-if="row.flag" :id="'input_' + $index" v-model="inputValue"
                                    placeholder="请输入属性值" style="width: 6rem;" size="small"
                                    @keyup.enter="handleInputConfirm($index)" @blur="handleInputConfirm($index)" />
                                <el-button v-else="!row.flag" class="button-new-tag" size="small" type="primary"
                                    @click="showInput($index)">
                                    +
                                </el-button>
                            </div>
                        </template>
                    </el-table-column>
                    <el-table-column align="center" label="操作" width="80">
                        <template #default="{ row }">
                            <el-button type="danger" icon="delete" @click="deleteSpuSaleAttr(row.baseSaleAttrId)"
                                label="删除" />
                        </template>
                    </el-table-column>
                </el-table>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="saveOrUpdate(ruleFormRef)" :loading="butLoading">保存</el-button>
                <el-button type="danger" @click="close">取消</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<script setup lang='ts' name="SpuFrom">
import type { BaseSaleAttrType, SpuSaleAttrType, SpuSaleAttrValueType, SpuType, SpuImageType } from '@/api/product/spu/type';
import type { Trademark } from '@/api/product/trademark/type';
import type { ResponseData } from '@/api/type';
import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'element-plus';
import spuConstant from './spuConstant';
import { reqTrademarkList, reqBaseSaleAttrList, reqSpuImageList, reqSpuSaleAttrList, reqSaveSpuInfo, reqUpdateSpuInfo } from '@/api/product/spu/index';

const props = defineProps(['category3', 'getPageData'])
const emit = defineEmits(['updateSpuScene']);
const dialogImageUrl = ref('')
const dialogVisible = ref(false)

// props 析构会导致失去响应式，可以使用computed动态计算出值
let category3 = computed(() => props.category3)

let spuFrom: SpuType = reactive({
    spuName: '',
    description: '',
    category3Id: category3,
    tmId: undefined,
    spuSaleAttrList: [],
    spuImageList: [],
});

let trademarkList = ref<Trademark[]>([])
let baseSaleAttrList = ref<BaseSaleAttrType[]>([])
let baseSaleAttrRef = ref<BaseSaleAttrType>()
let fileList = ref<UploadUserFile[]>([])
const inputValue = ref('')
let ruleFormRef = ref<FormInstance>();
let butLoading = ref(false);

const rules = ref<FormRules<SpuType>>({
    spuName: [{ required: true, message: 'SPU名称不能空', trigger: 'change' }],
    tmId: [{ required: true, message: '请选择SPU品牌', trigger: 'change' }],
    description: [{ required: true, message: 'SPU描述不能空', trigger: 'change' }],
    spuSaleAttrList: [{ required: true, message: 'SPU销售属性不能空', trigger: 'blur'}],
    spuImageList: [{ required: true, message: 'SPU照片不能空', trigger: 'change' }],
});

const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
    console.log(uploadFile, uploadFiles)
}

const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
    dialogImageUrl.value = uploadFile.url!
    dialogVisible.value = true
}

const handleError: UploadProps['onError'] = (file) => {
    ElMessage.error(`${file.name} 上传失败，请稍后再试。`)
}

const handleAvatarSuccess: UploadProps['onSuccess'] = (response) => {
    if (response.code === 200) {
        ruleFormRef.value?.clearValidate('spuImageList')
    } else {
        ElMessage.error('图片上传失败{}', response.message)
    }
}

const close = () => {
    baseSaleAttrRef.value = undefined
    fileList.value = []
    Object.assign(spuFrom, {
        id: undefined,
        spuName: '',
        description: '',
        tmId: null,
        spuSaleAttrList: [],
        spuImageList: [],
    })
    ruleFormRef.value?.resetFields()
    emit('updateSpuScene', spuConstant.scene_zero);
}

const getData = (spuDate: SpuType) => {
    // category3Id 是计算可得，不能覆盖
    const {category3Id, ...spuDateInfo} = spuDate;
    Object.assign(spuFrom, spuDateInfo)
    // 查询出SPU照片
    reqSpuImageList(spuDate.id as number).then(res => {
        spuFrom.spuImageList = res.data
        // 转换为fileList
        fileList.value = spuFrom.spuImageList.map(item => {
            return <UploadUserFile>{
                name: item.imgName,
                url: item.imgUrl,
                uid: item.id,
            }
        })
    }).catch((error: ResponseData<String>) => {
        ElMessage.error(('获取SPU照片失败。') + error.message);
    })
    // 查询出SPU属性
    reqSpuSaleAttrList(spuDate.id as number).then(res => {
        spuFrom.spuSaleAttrList = res.data
    }).catch((error: ResponseData<String>) => {
        ElMessage.error(('获取SPU销售属性失败。') + error.message);
    })
}


const unSelectSaleAttr = computed(() => {
    if (!spuFrom.spuSaleAttrList || spuFrom.spuSaleAttrList.length === 0) {
        return baseSaleAttrList.value;
    }
    const selectedSaleAttrIds = new Set(spuFrom.spuSaleAttrList.map(item => item.baseSaleAttrId));
    return baseSaleAttrList.value.filter(item => !selectedSaleAttrIds.has(item.id))
})


// 组件挂载加载下拉框数据
onMounted(() => {
    reqTrademarkList().then(res => {
        trademarkList.value = res.data
    }).catch((error: ResponseData<String>) => {
        ElMessage.error(('获取品牌列表失败。') + error.message);
    })

    reqBaseSaleAttrList().then(res => {
        baseSaleAttrList.value = res.data
    }).catch((error: ResponseData<String>) => {
        ElMessage.error(('获取销售属性列表失败。') + error.message);
    })
})

const deleteSpuSaleAttr = (id: number) => {
    const index = spuFrom.spuSaleAttrList.findIndex(item => item.baseSaleAttrId === id)
    spuFrom.spuSaleAttrList.splice(index, 1)
}


const handleClose = ($index: number, tag: SpuSaleAttrValueType) => {
    const dynamicTags = spuFrom.spuSaleAttrList[$index].spuSaleAttrValueList;
    dynamicTags.splice(dynamicTags.indexOf(tag), 1)
}

const showInput = ($index: number) => {
    spuFrom.spuSaleAttrList[$index].flag = true;
    nextTick(() => {
        document.getElementById(`input_${$index}`)!.focus()
    })
}

const handleInputConfirm = ($index: number) => {
    if (inputValue.value && inputValue.value.trim().length > 0) {
        if (spuFrom.spuSaleAttrList[$index].spuSaleAttrValueList.map(item => item.saleAttrValueName).includes(inputValue.value)) {
            ElMessage.error('该属性值已存在，请重新输入。')
            document.getElementById(`input_${$index}`)!.focus()
            return;
        }

        const tag: SpuSaleAttrValueType = {
            saleAttrValueName: inputValue.value,
            saleAttrName: spuFrom.spuSaleAttrList[$index].saleAttrName,
            isChecked: null,
            baseSaleAttrId: spuFrom.spuSaleAttrList[$index].baseSaleAttrId
        }
        spuFrom.spuSaleAttrList[$index].spuSaleAttrValueList.push(tag)
    }
    inputValue.value = ''
    spuFrom.spuSaleAttrList[$index].flag = false;
}

const addSaleAttr = () => {
    if (baseSaleAttrRef.value) {
        const saleAttr: SpuSaleAttrType = {
            baseSaleAttrId: baseSaleAttrRef.value.id,
            saleAttrName: baseSaleAttrRef.value.name,
            spuSaleAttrValueList: []
        }
        spuFrom.spuSaleAttrList.push(saleAttr)
        baseSaleAttrRef.value = undefined
        ruleFormRef.value?.clearValidate('spuSaleAttrList')
    }
}

const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
    if (!rawFile.type.startsWith('image/')) {
        ElMessage.error('请上传图片文件!')
        return false
    } else if (rawFile.size / 1024 / 1024 > 3) {
        ElMessage.error('图片文件大小不能超过3MB!')
        return false
    }
    return true
}

const saveOrUpdate = (ruleFormRef: FormInstance | undefined) => {
    if (!ruleFormRef) return
    // 照片转化为spuImageList
    spuFrom.spuImageList = fileList.value.map(item => {
        return <SpuImageType>{
            imgName: item.name,
            imgUrl: item.response ? (item.response as ResponseData<String>).data : item.url,
        }
    })
    ruleFormRef.validate().then(async () => {
        butLoading.value = true;
        const exitEmptySaleAttrName = spuFrom.spuSaleAttrList.find(item =>
            !item.spuSaleAttrValueList || item.spuSaleAttrValueList.length === 0
        );
        if (exitEmptySaleAttrName) {
            ElMessage.error(`${exitEmptySaleAttrName.saleAttrName}销售属性值不能为空。`)
            return;
        }
        try {
            if (spuFrom.id) {
                await reqUpdateSpuInfo(spuFrom)
            } else {
                await reqSaveSpuInfo(spuFrom)
            }
            ElMessage.success('SPU信息保存成功。');
            props.getPageData();
            close();

        } catch (error: unknown) {
            ElMessage.error((`SPU信息${spuFrom.id ? `更新` : `新增`}失败。`) + (error as ResponseData<String>).data);
        }
    }).catch(() => {
        ElMessage.error('请检查输入项是否正确。')
    }).finally(() => {
        butLoading.value = false;
    })
}


// 宏函数把数据交给外部
defineExpose({ getData })
</script>

<style scoped lang="scss">
.dynamic-tags {
    display: flex;
    gap: 0.5rem;
}
</style>